#include "NetWorkProDefine.h"
#include "SocketTimeOut.h"
#include <sys/fcntl.h>

void str_cli(FILE* fp, int sockfd);
int connect_nonb(int sockfd, const SA* saptr, socklen_t salen, int nsec);


int main(int argc, char** argv)
{
    printf("welcom to NBClietn!\n");
    if (argc < 3)
    {
        printf("usage : %s [ip] [command]\n",*argv);
        return 1;
    }
    
    int connectfd = socket(AF_INET, SOCK_STREAM, 0);
    if(connectfd < 0)
    {
        printf("socket error code :%u\n", errno);
        return 1;
    }

    struct sockaddr_in servaddr;
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    // inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
    // int connres = connect_timeout(connectfd, (SA*)&servaddr, sizeof(servaddr), 3);
    int connres = connect_nonb(connectfd, (SA*)&servaddr, sizeof(servaddr), 3);
    if(connres < 0)
    {
        printf("connect error :%s\n", strerror(errno));
        return 1;
    }

    const char* filepath = "./resource/demo.xml";
    FILE* fp = fopen(filepath, "r");
    if (NULL == fp)
    {
        printf("open error, file path :%s\n", filepath);
    }
    
    if ('1' == *argv[2])
    {
        str_cli(fp, connectfd);
    }
    else
    {
        str_cli(fp, connectfd);
    }
    
    return 0;
}



void str_cli(FILE* fp, int sockfd)
{
    pid_t pid;
    char szSend[MAXLEN] = {0}, szRecv[MAXLEN] = {0};

    if((pid = fork()) == 0)
    {
    /**
     * 子进程，仅仅读取服务器发送的消息
     * 
     */
        while (1)
        {
            memset(szRecv, 0, sizeof(szRecv));
            int recvlen = read(sockfd, szRecv, sizeof(szRecv));
            if(recvlen > 0)
            {
                // printf("server :%s\n", szRecv);
            }
            else if(recvlen < 0 && (EAGAIN == errno || EINTR == errno))
            {
                continue;
            }
            else
            {
                break;
            }
        }
        printf("end of read.\n");
        kill(getppid(), SIGTERM);//read失败回射服务不完整，可以关闭整个客户端
        exit(0);//系统关闭socket，这里也是使得sockfd引用次数减一
    }

    /**
     * 父进程，仅仅发送数据至服务器
     * 
     */
    while (fgets(szSend, sizeof(szSend), fp) != NULL)
    {
        if(write(sockfd, szSend, strlen(szSend)) < 0)
        {
            if(EAGAIN == errno || EINTR == errno)
            {
                printf("write again\n");
                write(sockfd, szSend, strlen(szSend));
            }
            else
            {
                printf("errno :%u\tmsg :%s\n", errno, strerror(errno));
                break;
            }
        }
        printf("client :%s\n", szSend);
        memset(szSend, 0, sizeof(szSend));
    }
    printf("end of fgets.\n");
    shutdown(sockfd, SHUT_WR);//告知服务端不要等我消息了
    //close(sockfd);//仅仅使sockfd引用次数减一，并没有不能关闭
    pause();//使得父进程在子进程退出后再退出，仅仅便于计算程序运行时间
}


int connect_nonb(int sockfd, const SA* saptr, socklen_t salen, int nsec)
{
    printf("connect_nonb\n");
    int flags, n, error;
    socklen_t len;
    fd_set rset, wset;
    struct timeval tval;

    flags = fcntl(sockfd, F_GETFL, 0);
    fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

    error = 0;
    if ((n = connect(sockfd, saptr, salen)) < 0)
    {
        if(errno != EINPROGRESS)
        {
            return -1;
        }
    }

    // Do whatever we want while the connect is taking place.
    /* 
    coding...........
    */

    if (n == 0)
    {
        goto connected;
    }

    FD_ZERO(&rset);
    FD_SET(sockfd, &rset);
    wset = rset;
    tval.tv_sec = nsec;
    tval.tv_usec = 0;
    if ((n = select(sockfd + 1, &rset, &wset, NULL, nsec ? &tval : NULL)) == 0)
    {
        close(sockfd);
        errno = ETIMEDOUT;
        return -1;
    }

    if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset))
    {
        len = sizeof(error);
        if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
        {
            return -1;
        }
    }
    else
    {
        printf("select error : sockfd not set\n");
    }
    
connected:
    fcntl(sockfd, F_SETFL, flags);//restore file status flags
    if (error)
    {
        close(sockfd);
        errno = error;
        return -1;
    }
    return 0;
}